﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Controls;
using System.Windows.Media;

namespace Magellan.Controls
{
    /// <summary>
    /// TextBlock which is visible to UI Automation
    /// </summary>
    public class UITextBlock : TextBlock
    {
        private static readonly DependencyPropertyKey isTextTrimmedKey = DependencyProperty.RegisterReadOnly(
            "IsTextTrimmed",
            typeof(bool),
            typeof(UITextBlock),
            new PropertyMetadata(false));

        private static readonly DependencyProperty isTextTrimmedProperty = isTextTrimmedKey.DependencyProperty;

        /// <summary>
        /// Ctor for UITextBlock
        /// </summary>
        public UITextBlock()
        {
            DefaultStyleKey = typeof(UITextBlock);

            SizeChanged += UITextBlockSizeChanged;
            Loaded += AddValueChangedToTextProperty;
            Unloaded += RemoveValueChangedToTextProperty;
        }

        private void RemoveValueChangedToTextProperty(object sender, RoutedEventArgs e)
        {
            if (e.OriginalSource != this)
                return;

            var textDescriptor = DependencyPropertyDescriptor.FromProperty(TextProperty, typeof(UITextBlock));
            textDescriptor.RemoveValueChanged(this, TextChanged);
        }

        private void AddValueChangedToTextProperty(object sender, RoutedEventArgs e)
        {
            var textDescriptor = DependencyPropertyDescriptor.FromProperty(TextProperty, typeof(UITextBlock));
            textDescriptor.AddValueChanged(this, TextChanged);
        }

        /// <summary>
        /// Returns true if text is trimmed
        /// </summary>
        public bool IsTextTrimmed
        {
            get { return GetIsTextTrimmed(this); }
        }

        /// <summary>
        /// Returns true if text is trimmed
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public static bool GetIsTextTrimmed(DependencyObject o)
        {
            return (bool)o.GetValue(isTextTrimmedProperty);
        }

        private static void SetIsTextTrimmed(DependencyObject target, bool value)
        {
            target.SetValue(isTextTrimmedKey, value);
        }

        private static void TextChanged(object sender, EventArgs e)
        {
            var textBlock = sender as TextBlock;
            if (null == textBlock)
            {
                return;
            }

            SetIsTextTrimmed(textBlock, TextTrimming.None != textBlock.TextTrimming && CalculateIsTextTrimmed(textBlock));
        }

        static void UITextBlockSizeChanged(object sender, SizeChangedEventArgs e)
        {
            var textBlock = sender as TextBlock;
            if (null == textBlock)
            {
                return;
            }

            SetIsTextTrimmed(textBlock, TextTrimming.None != textBlock.TextTrimming && CalculateIsTextTrimmed(textBlock));
        }

        /// <summary>
        /// Creates and returns an <see cref="T:System.Windows.Automation.Peers.AutomationPeer"/> object for this <see cref="T:System.Windows.Controls.TextBlock"/>.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Windows.Automation.Peers.AutomationPeer"/> object for this <see cref="T:System.Windows.Controls.TextBlock"/>.
        /// </returns>
        protected override AutomationPeer OnCreateAutomationPeer()
        {
            return new UITextBlockAutomationPeer(this);
        }

        private static bool CalculateIsTextTrimmed(TextBlock textBlock)
        {
            var typeface = new Typeface(
                textBlock.FontFamily,
                textBlock.FontStyle,
                textBlock.FontWeight,
                textBlock.FontStretch);
            // FormattedText is used to measure the whole width of the text held up by TextBlock container
            var formattedText = new FormattedText(
                textBlock.Text,
                System.Threading.Thread.CurrentThread.CurrentCulture,
                textBlock.FlowDirection,
                typeface,
                textBlock.FontSize,
                textBlock.Foreground)
            {
                MaxTextWidth = textBlock.ActualWidth
            };
            // When the maximum text width of the FormattedText instance is set to the actual
            // width of the textBlock, if the textBlock is being trimmed to fit then the formatted
            // text will report a larger height than the textBlock. Should work whether the
            // textBlock is single or multi-line.
            return (formattedText.Height > textBlock.ActualHeight);
        }
    }
}